package com.tulink.yichutu.biz;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tulink.yichutu.common.utils.DateUtils;
import com.tulink.yichutu.entity.WxUserEntity;
import com.tulink.yichutu.entity.WxVipDateEntity;
import com.tulink.yichutu.entity.WxVipOrderEntity;
import com.tulink.yichutu.service.WxUserService;
import com.tulink.yichutu.service.WxVipDateService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.BoundValueOperations;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 用户缓存业务方法
 *
 * @author wanchaopeng
 */
@Slf4j
@Component
public class VipDateBiz {
    private static ThreadLocal<DateFormat> threadLocal = new ThreadLocal<DateFormat>();
    public static DateFormat getEndTimeFormat() {
        DateFormat df = threadLocal.get();
        if (df == null) {
            df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            threadLocal.set(df);
        }
        return df;
    }

    private static final String vipDateCaches = "vip:%s";

    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private WxVipDateService wxVipDateService;

    public Boolean isLifetime(Date endTime) {
        return endTime != null && endTime.getYear() + 1900 > 2100;
    }

    public Date getVipEndTime(String userId) {
        try {
            String key = String.format(vipDateCaches, userId);
            Long endTime = getLongValue(redisTemplate.opsForValue().get(key));
            if(endTime == null) {
                return new Date(2021 - 1900, 10 - 1,6);
            }
            return new Date(endTime);
        } catch (Exception ex) {
            log.error("getVipEndTime error", ex);
        }
        return new Date(2021 - 1900, 10 - 1,6);
    }

    public Boolean isVip(Date vipEndTime) {
        return vipEndTime != null && vipEndTime.after(new Date());
    }

    public boolean updateEndTime(String userId, Integer days) {
        return updateEndTime(userId, days, true);
    }

    public boolean updateEndTime(String userId, Integer days, boolean fromNow) {
        WxVipDateEntity dateEntity = wxVipDateService.getOne(
                Wrappers.<WxVipDateEntity>lambdaQuery()
                        .eq(WxVipDateEntity::getUserId, userId)
        );
        if (dateEntity != null) {
            Date endTime = DateUtils.plusDays(
                    fromNow ? (new Date().before(dateEntity.getEndTime()) ? dateEntity.getEndTime() : new Date()) : dateEntity.getEndTime(),
                    days);
            boolean success = wxVipDateService.update(
                    Wrappers.<WxVipDateEntity>lambdaUpdate()
                            .set(WxVipDateEntity::getEndTime, endTime)
                            .eq(WxVipDateEntity::getEndTime, dateEntity.getEndTime())
                            .eq(WxVipDateEntity::getId, dateEntity.getId())
            );
            //乐观锁
            if (!success) {
                //抛出异常，回滚
                return false;
            }
            dateEntity.setEndTime(endTime);
        } else {
            dateEntity = new WxVipDateEntity();
            dateEntity.setUserId(userId);
            dateEntity.setStatus(1);
            dateEntity.setStartTime(new Date());
            LocalDateTime endTime = LocalDateTime.now();
            endTime = endTime.plusDays(days);
            dateEntity.setEndTime(Date.from(endTime.atZone(ZoneId.systemDefault()).toInstant()));
            wxVipDateService.save(dateEntity);
        }
        updateVipEndTime(dateEntity.getUserId(), dateEntity.getEndTime());
        return true;
    }

    @PostConstruct
    public void initCache() {
        int count = 0;
        int total = 0;
        int pageSize = 500;
        log.info("开始初始化VIP缓存...");
        redisTemplate.delete(vipDateCaches);
        while (count < 10000) {
            Page<WxVipDateEntity> page = new Page<>(count, pageSize);
            page = wxVipDateService.page(
                    page,
                    Wrappers.<WxVipDateEntity>lambdaQuery()
                            .select(WxVipDateEntity::getUserId, WxVipDateEntity::getEndTime)
                            .gt(WxVipDateEntity::getEndTime, new Date())
            );
            List<WxVipDateEntity> vipDateEntities = page.getRecords();
            if (vipDateEntities.size() > 0) {
                redisTemplate.executePipelined(new SessionCallback<Object>() {
                    @Override
                    public Object execute(RedisOperations redisOperations) throws DataAccessException {
                        vipDateEntities.forEach(vipDateEntity -> {
                            String key = String.format(vipDateCaches, vipDateEntity.getUserId());
                            BoundValueOperations boundValueOperations = redisOperations.boundValueOps(key);
                            boundValueOperations.set(vipDateEntity.getEndTime().getTime());
                            boundValueOperations.expireAt(vipDateEntity.getEndTime());
                        });
                        return null;
                    }
                });
                total += vipDateEntities.size();
                log.info("已加载VIP：{}", total);
            }
            if (vipDateEntities.size() < pageSize) {
                break;
            }
            count++;
        }
        log.info("初始化VIP缓存完成：{}", total);
    }

    private void updateVipEndTime(String userId, Date endTime) {
        try {
            String key = String.format(vipDateCaches, userId);
            BoundValueOperations boundValueOperations = redisTemplate.boundValueOps(key);
            boundValueOperations.set(endTime.getTime());
            boundValueOperations.expireAt(endTime);
        } catch (Exception ex) {
            log.error("updateVipEndTime cache error", ex);
        }
    }

    private Long getLongValue(Object obj) {
        try {
            if (obj instanceof Long) {
                return (Long) obj;
            } else if (obj instanceof Number) {
                return Long.valueOf(((Number) obj).longValue());
            } else if (obj instanceof String) {
                return Long.valueOf((String) obj);
            } else if (obj != null) {
                return Long.valueOf(obj.toString());
            }
        } catch (Exception ex) {

        }
        return null;
    }

}
